package com.vcc.boss.controller;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson2.JSONObject;
import com.mybatisflex.core.paginate.Page;
import com.vcc.core.annotations.RestPack;
import com.vcc.core.exception.ProgramException;
import com.vcc.core.util.RedisCustomizeUtils;
import com.vcc.service.boss.request.userStatistics.UserStatisticsVo;
import com.vcc.service.sa.BossUserUtil;
import com.vcc.service.service.*;
import jakarta.annotation.Resource;
import lombok.extern.log4j.Log4j2;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Log4j2
@RestController
@RequestMapping("userStatistics")
public class UserStatisticsController {

    @Resource
    private UserStatisticsService userStatisticsService;
    @Resource
    private UserService userService;
    @Resource
    private BossUserService bossUserService;
    @Resource
    private RechargeLogService rechargeLogService;
    @Resource
    private WithdrawService withdrawService;
    @Resource
    private CardOrderService cardOrderService;
    @Resource
    private TransactionService transactionService;


    @GetMapping("paging")
    @SaCheckPermission("client:user-statistics:view")
    @RestPack
    @SaCheckLogin
    public Page<UserStatisticsVo> paging(
            Integer pageNumber,
            Integer pageSize,
            String startDate,
            String endDate,
            Long targetUserId,
            Long dealerId
    ) {
        if (!BossUserUtil.hasAllData()) {
            if (targetUserId != null && targetUserId != 0L) {
                Long userDealerId = userService.getDealerIdById(targetUserId);
                if (!userDealerId.equals(BossUserUtil.getId())) {
                    throw new ProgramException("无权查看该用户的统计信息");
                }
            }
            //判断当前选择的 dealerId 是不是自己或者自己的下级
            if (dealerId != null && dealerId != 0L) {
                Long parentId = bossUserService.getParentIdById(dealerId);
                //不是自己也不是自己的下级
                if (!dealerId.equals(BossUserUtil.getId()) && !parentId.equals(BossUserUtil.getId())) {
                    throw new ProgramException("无权查看该经销商的用户统计信息");
                }
            }
        }
        return userStatisticsService.paging(pageNumber, pageSize, startDate, endDate, targetUserId, null, dealerId);
    }





    @GetMapping("getData")
    @RestPack
    @SaCheckLogin
    public List<UserStatisticsVo> getData(
            String startDate,
            String endDate,
            Long targetUserId,
            Long dealerId
    ) {
        if (!BossUserUtil.hasAllData()) {
            if (targetUserId != null && targetUserId != 0L) {
                Long userDealerId = userService.getDealerIdById(targetUserId);
                if (!userDealerId.equals(BossUserUtil.getId())) {
                    throw new ProgramException("无权查看该用户的统计信息");
                }
            }
            //判断当前选择的 dealerId 是不是自己或者自己的下级
            if (dealerId != null && dealerId != 0L) {
                Long parentId = bossUserService.getParentIdById(dealerId);
                //不是自己也不是自己的下级
                if (!dealerId.equals(BossUserUtil.getId()) && !parentId.equals(BossUserUtil.getId())) {
                    throw new ProgramException("无权查看该经销商的用户统计信息");
                }
            }else {
                dealerId = BossUserUtil.getId();
            }
        }
        DateTime startDateTime = DateUtil.parse(startDate, "yyyy-MM-dd");
        DateTime endDateTime = DateUtil.parse(endDate, "yyyy-MM-dd");
        //判断开始时间是否在结束时间之后
        if (startDateTime.isAfter(endDateTime)) {
            throw new ProgramException("开始时间不能在结束时间之后");
        }
        //判断开始时间是否在2025-8-10之前
        DateTime minDate = DateUtil.parse("2025-08-01");
        if (startDateTime.isBefore(minDate)) {
            startDateTime = minDate;
        }
        DateTime now = DateUtil.date();
        DateTime lastDate = DateUtil.parse(userStatisticsService.getLastDate(), "yyyy-MM-dd");
        log.info("数据库最后统计时间: {}", lastDate.toString("yyyy-MM-dd"));
        log.info("查询开始时间:{}，结束时间:{},当前时间: {}", startDate, endDate, now.toString("yyyy-MM-dd"));
        // 结束时间都在lastDate之前就直接返回
        // 如果存在统计标记，则统计日期不算
        if (RedisCustomizeUtils.hasKey("calculate:account")){
            //正在统计前一天数据
            lastDate = lastDate.offset(DateField.DAY_OF_MONTH, -1);
            log.info("存在统计标记，最后统计时间向前推一天: {}", lastDate.toString("yyyy-MM-dd"));
        }
        if (endDateTime.isBeforeOrEquals(lastDate)) {
            log.info("结束时间在最后统计时间之内，无需补充临时数据");
            // 情况1：全部日期都在lastDate之前或等于 → 查询历史数据
            return userStatisticsService.listByDate(startDateTime.toString("yyyy-MM-dd"), endDateTime.toString("yyyy-MM-dd"), targetUserId, dealerId);
        } else if (startDateTime.isAfter(lastDate)) {
            log.info("开始时间和结束时间都在最后统计时间之后，直接查询实时数据");
            // 情况2：全部日期都在lastDate之后 → 查询实时数据
            return queryRealTimeData(startDateTime, endDateTime, targetUserId, dealerId, new ArrayList<>()).stream().sorted(
                    Comparator.comparing(UserStatisticsVo::getDate)
            ).collect(Collectors.toList());
        } else {
            log.info("开始时间在最后统计时间之前，结束时间在最后统计时间之后，混合查询");
            // 情况3：日期范围跨越lastDate → 混合查询
            // 如果存在
            // 历史部分：startDate 到 lastDate
            List<UserStatisticsVo> userStatisticsVos = userStatisticsService.listByDate(startDateTime.toString("yyyy-MM-dd"), lastDate.toString("yyyy-MM-dd"), targetUserId, dealerId);
            // 实时部分：lastDate加1天 到 endDate
            return queryRealTimeData(lastDate.offset(DateField.DAY_OF_MONTH, 1), DateUtil.endOfDay(endDateTime), targetUserId, dealerId, userStatisticsVos).stream().sorted(
                    Comparator.comparing(UserStatisticsVo::getDate)
            ).collect(Collectors.toList());
        }
    }


    private List<UserStatisticsVo> queryRealTimeData(DateTime startDate, DateTime endDate, Long targetUserId, Long dealerId, List<UserStatisticsVo> userStatisticsVos) {
        List<JSONObject> accountRecharge = rechargeLogService.statistics(startDate, endDate, targetUserId, dealerId);
        List<JSONObject> accountWithdraw = withdrawService.statistics(startDate, endDate, targetUserId, dealerId);
        List<JSONObject> cardOrderRechargeAndWithdraw = cardOrderService.statistics(startDate, endDate, targetUserId, dealerId);
        List<JSONObject> statistics = transactionService.statistics(startDate, endDate, targetUserId, dealerId);
        //获取所有的date
        Set<String> dates = new HashSet<>();
        dates.addAll(accountRecharge.stream().map(it -> it.getString("date")).toList());
        dates.addAll(accountWithdraw.stream().map(it -> it.getString("date")).toList());
        dates.addAll(cardOrderRechargeAndWithdraw.stream().map(it -> it.getString("date")).toList());
        dates.addAll(statistics.stream().map(it -> it.getString("date")).toList());
        for (String date : dates) {
            JSONObject recharge = accountRecharge.stream().filter(it -> it.getString("date").equals(date)).findFirst().orElse(null);
            JSONObject withdraw = accountWithdraw.stream().filter(it -> it.getString("date").equals(date)).findFirst().orElse(null);
            JSONObject cardOrder = cardOrderRechargeAndWithdraw.stream().filter(it -> it.getString("date").equals(date)).findFirst().orElse(null);
            JSONObject transaction = statistics.stream().filter(it -> it.getString("date").equals(date)).findFirst().orElse(null);
            UserStatisticsVo userStatisticsVo = UserStatisticsVo.builder()
                    .userId(null)
                    .date(date)
                    .accountRechargeAmount(recharge == null ? BigDecimal.ZERO : recharge.getBigDecimal("successAmount"))
                    .accountRechargeCount(recharge == null ? 0 : recharge.getIntValue("successCount"))
                    .accountWithdrawAmount(withdraw == null ? BigDecimal.ZERO : withdraw.getBigDecimal("successAmount"))
                    .accountWithdrawCount(withdraw == null ? 0 : withdraw.getIntValue("successCount"))
                    .cardRechargeAmount(cardOrder == null ? BigDecimal.ZERO : cardOrder.getBigDecimal("rechargeAmount"))
                    .cardRechargeCount(cardOrder == null ? 0 : cardOrder.getIntValue("rechargeCount"))
                    .cardWithdrawAmount(cardOrder == null ? BigDecimal.ZERO : cardOrder.getBigDecimal("withdrawAmount"))
                    .cardWithdrawCount(cardOrder == null ? 0 : cardOrder.getIntValue("withdrawCount"))
                    .cardConsumeAmount(transaction == null ? BigDecimal.ZERO : transaction.getBigDecimal("sumAmount"))
                    .cardConsumeSuccessAmount(transaction == null ? BigDecimal.ZERO : transaction.getBigDecimal("successAuthAmount"))
                    .cardConsumeFailAmount(transaction == null ? BigDecimal.ZERO : transaction.getBigDecimal("failedAuthAmount"))
                    .cardConsumeCount(transaction == null ? 0 : transaction.getIntValue("sumCount"))
                    .cardConsumeSuccessCount(transaction == null ? 0 : transaction.getIntValue("successAuthCount"))
                    .cardConsumeFailCount(transaction == null ? 0 : transaction.getIntValue("failedAuthCount"))
                    .cardRefundAmount(transaction == null ? BigDecimal.ZERO : transaction.getBigDecimal("refundAmount"))
                    .cardRefundCount(transaction == null ? 0 : transaction.getIntValue("refundCount"))
                    .cardReversalAmount(transaction == null ? BigDecimal.ZERO : transaction.getBigDecimal("reversalAmount"))
                    .cardReversalCount(transaction == null ? 0 : transaction.getIntValue("reversalCount"))
                    .verifyCount(transaction == null ? 0 : transaction.getIntValue("verifyCount"))
                    .openCardCount(cardOrder == null ? 0 : cardOrder.getIntValue("openCount"))
                    .closeCardCount(cardOrder == null ? 0 : cardOrder.getIntValue("closeCount"))
                    .registerCount(0)
                    .build();
            userStatisticsVos.add(userStatisticsVo);
        }
        return userStatisticsVos;
    }
}